﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using Kernel;
using ElectionOfLeaderAlg.Controllers;

namespace ElectionOfLiderAlg
{
    public class ElectionRingTree : IStepController
    {
        private readonly Graph _graph;
        private int _step = 0;        

        // рассылка всем соседям  сообщений побудки не более одного раза
        private Dictionary<int, bool> WSp;
        // подсчет количества принятных сообщений побудки
        private Dictionary<int, int> WRp;
        // текущий минимальный отличительный признак
        private Dictionary<int, int> Vp;


        /// <summary>
        /// Создаёт контроллер для древесного алгоритма
        /// </summary>
        /// <param name="initiatorCount">количество инициаторов</param>
        /// <param name="depth">глубина дерева</param>
        /// <param name="minChildCount">минимальное число потомков у каждой вершины</param>
        /// <param name="maxChildCount">максимальное число потомков у каждой вершины</param>
        public ElectionRingTree(int depth, int minChildCount, int maxChildCount, int initiatorCount, RichTextBox rtbLog)
        {
            double nodeCount = 0;
            for (int i = 1; i <= depth; ++i)
                nodeCount += Math.Pow(minChildCount, i);

            if (initiatorCount >= nodeCount)
                throw new Exception("Инициаторов не может быть больше чем вершин в графе");

            _graph = GraphGenerator.CreateTree(depth, minChildCount, maxChildCount);

            Random rand = new Random();
            int colI = 0;
            int randIn = 0;
            while (colI != initiatorCount)
            {
                randIn = rand.Next(0, _graph.Nodes.Count - 1);
                if (!_graph.Nodes[randIn].IsInitiator)
                {
                    colI++;
                    _graph.Nodes[randIn].IsInitiator = true;
                }

            }

            WSp = new Dictionary<int, bool>(_graph.Nodes.Count);
            WRp = new Dictionary<int, int>(_graph.Nodes.Count);
            Vp = new Dictionary<int, int>(_graph.Nodes.Count);

            foreach (var node in _graph.Nodes)
            {
                WSp.Add(node.Number, false);
                WRp.Add(node.Number, 0);
                Vp.Add(node.Number, node.Number);
                node.rtbLog = rtbLog;
                if (node.IsInitiator) 
                    node.State = GraphNode.NodeState.Cand;
                else
                    node.State = GraphNode.NodeState.Sleep;
            }           
        }


        public Graph NextStep()
        {  
            //принимаем все сообщения, отправленные на предыдущем шаге
            _graph.Nodes.ForEach(x => x.ReceiveMessages());

            // рассылка инициаторами сообщений побудки
            if (_step == 0)
                _graph.Nodes.Where(x => x.IsInitiator).ToList().ForEach(y =>
                {
                    WSp[y.Number] = true;
                    foreach (var child in y.Children)
                        y.SendMessage(child, new Msg(0, y.Number, MessageType.wakeup));
                });

            foreach (var node in _graph.Nodes)
            {
                if (WRp[node.Number] < node.Children.Count) // не закончен этап побудки
                {
                    while (node.ReceivedMessages.Count != 0 && node.ReceivedMessages.Last().mesType == MessageType.wakeup)
                    {
                        Msg tmp = node.GetMessage();
                        WRp[node.Number]++;
                        if (!WSp[node.Number]) // не отсылал сообщений побудки
                        {
                            WSp[node.Number] = true;
                            foreach (var child in node.Children)
                                node.SendMessage(child, new Msg(0, node.Number, MessageType.wakeup));
                        }
                    }
                }
                else // начало работы древесного алгоритма
                {
                    // получил от всех (кроме одного) сообщение с меткой
                    while (node.Rec.Where(x => (x.Value == 0)).Count() > 1 && node.ReceivedMessages.Count != 0)
                    {
                        Msg tmp = node.GetMessage();
                        if (tmp.mesType == MessageType.wakeup)
                            throw new Exception("22");

                        if (node.Rec[tmp.sender_id.ToString()] == 1)
                            throw new Exception("11");

                        node.Rec[tmp.sender_id.ToString()] = 1;
                        Vp[node.Number] = (Vp[node.Number] < tmp.number) ? Vp[node.Number] : tmp.number;
                    }
 
                    if (node.Rec.Where(x => (x.Value == 0)).Count() == 1)
                    {
                        var recipient = _graph.GetNodeByName(node.Rec.Where(x => (x.Value == 0)).Single().Key);
                        if (node.Snd[recipient.Name] == 1)
                            node.SendMessage(recipient, new Msg(Vp[node.Number], node.Number, MessageType.tok));

                        if (node.ReceivedMessages.Count != 0)
                        {
                            Msg tmp = node.GetMessage();
                            node.Rec[tmp.sender_id.ToString()] = 1;
                            Vp[node.Number] = (Vp[node.Number] < tmp.number) ? Vp[node.Number] : tmp.number;

                            if (Vp[node.Number] == node.Number)
                                node.State = GraphNode.NodeState.Leader;
                            else
                                node.State = GraphNode.NodeState.Lost;

                            node.Children.Where(x => x.Number != tmp.sender_id).ToList().ForEach(y =>
                               node.SendMessage(y, new Msg(Vp[node.Number], node.Number, MessageType.tok))
                                );
                        }
                    }
                }
            }

            _step++;

            return _graph;
        }


        public bool IsEnd()
        {            
            foreach (var node in _graph.Nodes)
            {
                if (node.State != GraphNode.NodeState.Leader && node.State != GraphNode.NodeState.Lost)
                    return false;
            }
            return true;
            //return _graph.Nodes.Any(x => x.State == GraphNode.NodeState.Leader);
        }


        public Graph GetCurrentState()
        {
            return _graph;
        }

    }

}
